﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GMUtilitiesDotNet.Threading;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ThouShaltNotBrick.Models;
using ThouShaltNotBrickLevelEditor.Code.GeneralObjects;
using System.Windows;
using ThouShaltNotBrickLevelEditor.Models;
using ThouShaltNotBrickLevelEditor.Code.Helpers;

namespace ThouShaltNotBrickLevelEditor.Code.Renderers
{
    internal sealed class BrickStateSelectorRenderer : IWorker<BrickStateSelectorRendererParameters, BrickStateSelectorRendererReturnValue>
    {
        #region Constants
        private const double BRICK_STATE_PADDING = 5.0;
        private const double PADDED_BRICK_WIDTH = Constants.BRICK_WIDTH + BRICK_STATE_PADDING * 2.0;
        private const double PADDED_BRICK_HEIGHT = Constants.BRICK_HEIGHT + BRICK_STATE_PADDING * 2.0;
        private const double SELECTION_RECT_OUTLINE_OFFSET = BRICK_STATE_PADDING / 2.0;
        #endregion

        #region Overriden Methods
        public BrickStateSelectorRendererReturnValue Work(BrickStateSelectorRendererParameters pParameters)
        {
            if (pParameters == null ||
                pParameters.BrickStatesTypesImages == null)
            {
                return null;
            }

            if (pParameters.ControlWidth < 1.0 || pParameters.ControlHeight < 1.0)
            {
                return null;
            }

            List<BrickSelectorRectWithImage> brickSelectorRectWithImageList = GetBrickSelectorRectWithImageList(pParameters);

            DrawingVisual dv = new DrawingVisual();
            using (DrawingContext dc = dv.RenderOpen())
            {
                Draw(
                    dc,
                    brickSelectorRectWithImageList,
                    pParameters.BrickStateSelection);
            }

            RenderTargetBitmap brickStateSelectorBitmap = RendererHelpers.CreateRenderTargetBitmap(
                (int)pParameters.ControlWidth,
                (int)pParameters.ControlHeight);

            brickStateSelectorBitmap.Render(dv);
            brickStateSelectorBitmap.Freeze();

            List<BrickSelectorRect> brickSelectorRectList = brickSelectorRectWithImageList.Select(
                bsrwil => bsrwil.BrickSelectorRect).ToList();
            BrickSelectorRects brickSelectorRects = new BrickSelectorRects(brickSelectorRectList);

            return new BrickStateSelectorRendererReturnValue(
                brickStateSelectorBitmap,
                brickSelectorRects);
        }
        #endregion

        #region Methods
        private static void Draw(
            DrawingContext pDc,
            List<BrickSelectorRectWithImage> pBrickSelectorRectWithImageList,
            BrickStateSelection pBrickStateSelection)
        {
            foreach (BrickSelectorRectWithImage brickSelectorRectWithImage in pBrickSelectorRectWithImageList)
            {
                BrickSelectorRect brickSelectorRect = brickSelectorRectWithImage.BrickSelectorRect;
                pDc.DrawImage(
                    brickSelectorRectWithImage.BrickStateImage,
                    brickSelectorRect.BrickStateRect);

                if (pBrickStateSelection != null &&
                    brickSelectorRect.BrickTypeName == pBrickStateSelection.BrickTypeName &&
                    brickSelectorRect.BrickStateName == pBrickStateSelection.BrickStateName)
                {
                    DrawBrickStateSelection(pDc, brickSelectorRect.BrickStateRect);
                }
            }
        }

        private static void DrawBrickStateSelection(
            DrawingContext pDc,
            Rect pBrickStateRect)
        {
            Rect selectionRect = Rect.Inflate(pBrickStateRect, SELECTION_RECT_OUTLINE_OFFSET, SELECTION_RECT_OUTLINE_OFFSET);

            Pen pen = new Pen(Brushes.Red, 2.0);
            pDc.DrawRectangle(null, pen, selectionRect);
        }

        private static List<BrickSelectorRectWithImage> GetBrickSelectorRectWithImageList(
            BrickStateSelectorRendererParameters pParameters)
        {
            List<BrickSelectorRectWithImage> brickSelectorRectWithImageList = new List<BrickSelectorRectWithImage>();

            BrickStatesTypesImages brickStatesTypesImages = pParameters.BrickStatesTypesImages;
            BrickTypesData brickTypesData = brickStatesTypesImages.BrickTypesData;
            BrickStatesData brickStatesData = brickStatesTypesImages.BrickStatesData;
            BrickStateImages brickStateImages = brickStatesTypesImages.BrickStateImages;

            List<BrickTypeData> brickTypeDataList = brickTypesData.GetBrickTypes();
            for (int i = 0; i < brickTypeDataList.Count; i++)
            {
                BrickTypeData brickTypeData = brickTypeDataList[i];
                List<string> brickStateNames = brickTypeData.GetBrickStates();
                for (int j = 0; j < brickStateNames.Count; j++)
                {
                    Rect brickStateRect = GetBrickStateRect(i, j);

                    BrickSelectorRect brickSelectorRect = new BrickSelectorRect(
                        brickTypeData,
                        j,
                        brickStateRect);
                    BitmapImage brickStateImage = brickStateImages.GetBrickStateImage(brickStateNames[j]);

                    BrickSelectorRectWithImage brickSelectorRectWithImage = new BrickSelectorRectWithImage(
                        brickSelectorRect,
                        brickStateImage);

                    brickSelectorRectWithImageList.Add(brickSelectorRectWithImage);
                }
            }

            return brickSelectorRectWithImageList;
        }

        private static Rect GetBrickStateRect(int pBrickTypeIndex, int pBrickStateIndex)
        {
            return new Rect(
                BRICK_STATE_PADDING + pBrickTypeIndex * PADDED_BRICK_WIDTH,
                BRICK_STATE_PADDING + pBrickStateIndex * PADDED_BRICK_HEIGHT,
                Constants.BRICK_WIDTH,
                Constants.BRICK_HEIGHT);
        }
        #endregion

        #region Inner Classes
        private sealed class BrickSelectorRectWithImage
        {
            #region Properties
            public BrickSelectorRect BrickSelectorRect { get; private set; }
            public BitmapImage BrickStateImage { get; private set; }
            #endregion

            #region Constructors
            public BrickSelectorRectWithImage(
                BrickSelectorRect pBrickSelectorRect,
                BitmapImage pBrickStateImage)
            {
                BrickSelectorRect = pBrickSelectorRect;
                BrickStateImage = pBrickStateImage;
            }
            #endregion
        }
        #endregion
    }
}